// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.linqingying.cangnova.ide.impl.util.newProjectWizard

import com.intellij.diagnostic.PluginException
import com.intellij.ide.IdeCoreBundle
import com.intellij.ide.highlighter.ModuleFileType
import com.intellij.ide.highlighter.ProjectFileType
import com.intellij.ide.projectWizard.NewProjectWizardCollector.logGeneratorSelected
import com.intellij.ide.projectWizard.projectTypeStep.ProjectTypeList
import com.intellij.ide.util.projectWizard.*
import com.intellij.ide.util.projectWizard.ModuleBuilder.ModuleConfigurationUpdater
import com.intellij.ide.wizard.*
import com.intellij.ide.wizard.GeneratorNewProjectWizardBuilderAdapter.Companion.NPW_PREFIX
import com.intellij.ide.wizard.language.BaseLanguageGeneratorNewProjectWizard
import com.intellij.ide.wizard.language.LanguageGeneratorNewProjectWizard
import com.intellij.ide.wizard.language.LegacyLanguageGeneratorNewProjectWizard
import com.intellij.internal.statistic.utils.getPluginInfo
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.StorageScheme
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.ExtensionPointListener
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.PluginDescriptor
import com.intellij.openapi.module.ModuleType
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.platform.ProjectTemplate
import com.intellij.platform.ProjectTemplateEP
import com.intellij.platform.ProjectTemplatesFactory
import com.intellij.platform.templates.ArchivedProjectTemplate
import com.intellij.platform.templates.ArchivedTemplatesFactory
import com.intellij.platform.templates.LocalArchivedTemplate
import com.intellij.platform.templates.TemplateModuleBuilder
import com.intellij.ui.JBCardLayout
import com.intellij.ui.JBColor
import com.intellij.ui.OnePixelSplitter
import com.intellij.ui.components.JBPanelWithEmptyText
import com.intellij.ui.dsl.builder.panel
import com.intellij.util.containers.FactoryMap
import com.intellij.util.containers.MultiMap
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.linqingying.cangnova.CangNovaUIBundle.message
import com.linqingying.cangnova.ide.impl.projectWizard.ProjectCategory
import com.linqingying.cangnova.ide.impl.projectWizard.ProjectTemplateList
import com.linqingying.cangnova.ide.impl.projectWizard.TemplateBasedCategory
import com.linqingying.cangnova.ide.impl.projectWizard.projectTypeStep.LanguageGeneratorItem
import com.linqingying.cangnova.ide.impl.projectWizard.projectTypeStep.TemplateGroupItem
import com.linqingying.cangnova.ide.impl.projectWizard.projectTypeStep.UserTemplateGroupItem
import com.linqingying.cangnova.ide.impl.templates.BuilderBasedTemplate
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import java.awt.CardLayout
import java.awt.Dimension
import java.io.File
import java.nio.file.Paths
import java.util.function.Consumer
import java.util.function.Function
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.event.ListSelectionEvent
import javax.swing.event.ListSelectionListener
import kotlin.math.max

class CangNovaProjectTypeStep(
  private val context: WizardContext,
  val wizard: CangNovaNewProjectWizard,
  val modulesProvider: ModulesProvider,
) : ModuleWizardStep(), SettingsStep, Disposable {
  private val templatesMap: MultiMap<TemplatesGroup, ProjectTemplate> = MultiMap.createLinked()
  private val customSteps: Map<String, ModuleWizardStep> = HashMap()

  val headerPanel = panel {
    row { label("这是HarderPanel") }

  }

  // 创建 frameworksPanelPlaceholder
  val frameworksPanelPlaceholder = panel {
    row {
      label("Additional Libraries and Frameworks") // 替代 XML 中的绑定文本
    }
  }
  private val projectTypeList: ProjectTypeList = ProjectTypeList(context).apply {
    installFilteringListModel(getNamer(), getEmptyStatusPresenter())
    // These "fill" methods are stateful. The "fillGroupTemplateMap" method reads and modifies the "templatesMap".
    setTemplateGroupItems(fillGroupTemplateMap(context))
    setLanguageGeneratorItems(fillLanguageGeneratorTemplateMap(context))
    setUserTemplateGroupItems(fillUserTemplateMap(context))
  }
  val templateListPanel: ProjectTemplateList = ProjectTemplateList()

  val optionsPanel = panel {

    row { cell(templateListPanel) }
    row { cell(frameworksPanelPlaceholder) } // 添加到 optionsPanel
    row {
      cell(JBPanelWithEmptyText().apply {
        withEmptyText(message("label.select.project.type.to.configure"))
      })
    }
  }.apply {
    layout = JBCardLayout()
  }
  val settingsPanel: JPanel = panel {
    row {
      cell(headerPanel)

    }
    row {
      cell(optionsPanel)
    }
  }

  val projectTypePanel: JPanel = panel {
    row {
      cell(projectTypeList.component)
    }

  }.apply {
    minimumSize = Dimension(240, 100)

  }


  val panel: JComponent = panel {
    row {

      cell(OnePixelSplitter(false, 0.25f).apply {
        setHonorComponentsMinimumSize(true)
        firstComponent = projectTypePanel
        secondComponent = settingsPanel
      })

      //cell(settingsPanel)

    }

  }.apply {

  }
  private val configurationUpdater: ModuleConfigurationUpdater

  private val builders: Map<ProjectTemplate, ModuleBuilder> = FactoryMap.create { key: ProjectTemplate -> key.createModuleBuilder() as ModuleBuilder }
  private var settingsStep: ModuleWizardStep? = null

  init {
    context.addContextListener(object : WizardContext.Listener {
      override fun switchToRequested(placeId: String, configure: Consumer<in Step?>) {

        val groupToSelect = templatesMap.keySet().find { it.id == placeId }

        if (groupToSelect != null) {
          projectTypeList.setSelectedTemplateGroup(groupToSelect)
          try {
            configure.accept(getCustomStep())
          }
          catch (exception: Throwable) {
            throw IllegalStateException("Cannot switch on $placeId, current step $currentCard", exception)
          }
        }
      }
    })


    installLanguageGeneratorWatcher(context)
    //val layout = panel.layout as GridLayoutManager
    //layout.hGap = 0
    //panel.layout = layout
    //val splitter: JBSplitter = OnePixelSplitter(false, 0.25f)
    //splitter.setHonorComponentsMinimumSize(true)
    //splitter.firstComponent = projectTypePanel
    //splitter.secondComponent = settingsPanel

    //panel.removeAll()
    //panel.add(splitter, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH,
    //                                    GridConstraints.FILL_BOTH,
    //                                    GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW,
    //                                    GridConstraints.SIZEPOLICY_WANT_GROW,
    //                                    null, null, null))

    //
    val panelWithEmptyText = JBPanelWithEmptyText()
      .withEmptyText(message("label.select.project.type.to.configure"))
    optionsPanel.add(panelWithEmptyText, EMPTY_CARD)

    settingsPanel.border = JBUI.Borders.customLine(JBColor.border(), 1, 0, 1, 0)


    val project = context.project


    //final LibrariesContainer container = LibrariesContainerFactory.createContainer(context, modulesProvider);
    //FrameworkSupportModelBase model = new FrameworkSupportModelBase(project, null, container) {
    //  @NotNull
    //  @Override
    //  public String getBaseDirectoryForLibrariesPath() {
    //    var builder = getSelectedBuilder();
    //    var contentEntryPath = ObjectUtils.doIfNotNull(builder, it -> it.getContentEntryPath());
    //    return StringUtil.notNullize(contentEntryPath);
    //  }
    //
    //  @Override
    //  public ModuleBuilder getModuleBuilder() {
    //    return getSelectedBuilder();
    //  }
    //};
    //myFrameworksPanel = new AddSupportForFrameworksPanel(Collections.emptyList(), model, true, myHeaderPanel);
    //Disposer.register(this, myFrameworksPanel);
    //frameworksPanelPlaceholder.add(myFrameworksPanel.getMainPanel());
    //myFrameworksLabel.setLabelFor(myFrameworksPanel.getFrameworksTree());
    //frameworksLabel.setBorder(JBUI.Borders.empty(3))
    //
    configurationUpdater = object : ModuleConfigurationUpdater() {


      override fun update(module: com.intellij.openapi.module.Module, rootModel: ModifiableRootModel) {
        if (isFrameworksMode()) {
          //myFrameworksPanel.addSupport(module, rootModel);
        }
      }
    }

    projectTypeList.whenProjectTemplateGroupSelected(Consumer<TemplatesGroup> { group: TemplatesGroup? ->
      group?.let { projectTypeChanged(it) }
    })

    templateListPanel.addListSelectionListener(ListSelectionListener { event: ListSelectionEvent ->
      if (!event.valueIsAdjusting) {
        updateSelection()
      }
    })

    for (templatesGroup in templatesMap.keySet()) {
      val builder = templatesGroup.moduleBuilder
      if (builder != null) {
        wizard.sequence.addStepsForBuilder(builder, context, modulesProvider)
      }
      for (template in templatesMap.get(templatesGroup)) {
        builders.get(template)?.let { wizard.sequence.addStepsForBuilder(it, context, modulesProvider) }
      }
    }

    projectTypeList.restoreSelection()
    templateListPanel.restoreSelection()
  }

  private fun setTemplatesList(group: TemplatesGroup) {
    val list = java.util.ArrayList<ProjectTemplate>(templatesMap.get(group))
    val moduleBuilder = group.moduleBuilder
    if (moduleBuilder != null && moduleBuilder !is TemplateModuleBuilder) {
      list.add(0, BuilderBasedTemplate(moduleBuilder))
    }
    templateListPanel.setTemplates(list, false)
  }

  private fun showTemplates(group: TemplatesGroup) {
    setTemplatesList(group)
    showCard(TEMPLATES_CARD)
  }

  private fun isFrameworksMode(): Boolean {
    return FRAMEWORKS_CARD == currentCard && getSelectedBuilder() == context.projectBuilder
  }

  private fun showCustomOptions(builder: ModuleBuilder): Boolean {
    val card = builder.builderId

    var step = customSteps.get(card)
    //if (step == null) {
    //  step = builder.getCustomOptionsStep(context, this);
    //  if (builder is TemplateModuleBuilder) {
    //    step =   ProjectSettingsStep(context);
    //    context.setProjectBuilder(builder);
    //    NewProjectWizardCollector.logCustomTemplateSelected(context);
    //  }
    //
    //  if (step == null) return false;
    //
    //  context.setProjectBuilder(builder);
    //  step.updateStep();
    //
    //  JComponent component = step.getComponent();
    //  if (!(builder instanceof PromoModuleBuilder)) {
    //    component = new JBScrollPane(component);
    //    component.setBorder(JBUI.Borders.empty());
    //  }
    //
    //  myOptionsPanel.add(component, card);
    //
    //  myCustomSteps.put(card, step);
    //}

    try {
      step!!._init()
    }
    catch (e: Throwable) {

      LOG.error(e)
    }
    context.screen = 1

    showCard(card!!)

    return true
  }

  private fun showFrameworks(group: TemplatesGroup, moduleBuilder: ModuleBuilder) {
    val category = group.projectCategory

    //List<FrameworkSupportInModuleProvider> providers = FrameworkSupportUtil.getProviders(moduleBuilder);
    //if (category != null) {
    //  List<FrameworkSupportInModuleProvider> filtered = ContainerUtil.filter(providers, provider -> matchFramework(category, provider));
    //  // add associated
    //  Map<String, FrameworkSupportInModuleProvider> map = ContainerUtil.newMapFromValues(providers.iterator(), o -> o.getId());
    //  Set<FrameworkSupportInModuleProvider> set = new HashSet<>(filtered);
    //  for (FrameworkSupportInModuleProvider provider : filtered) {
    //    for (FrameworkSupportInModuleProvider.FrameworkDependency depId : provider.getDependenciesFrameworkIds()) {
    //      FrameworkSupportInModuleProvider dependency = map.get(depId.getFrameworkId());
    //      if (dependency == null) {
    //        if (!depId.isOptional()) {
    //          LOG.error("Cannot find provider '" + depId.getFrameworkId() + "' which is required for '" + provider.getId() + "'");
    //        }
    //        continue;
    //      }
    //      set.add(dependency);
    //    }
    //  }
    //
    //  myFrameworksPanel.setProviders(new ArrayList<>(set),
    //                                 Set.of(category.getAssociatedFrameworkIds()),
    //                                 Set.of(category.getPreselectedFrameworkIds()));
    //}
    //else {
    //  myFrameworksPanel.setProviders(providers);
    //}
    moduleBuilder.addModuleConfigurationUpdater(configurationUpdater)

    showCard(FRAMEWORKS_CARD)
  }

  // new TemplatesGroup selected
  private fun projectTypeChanged(group: TemplatesGroup) {
    val groupModuleBuilder = group.moduleBuilder

    settingsStep = null
    headerPanel.removeAll()
    if (groupModuleBuilder != null && groupModuleBuilder.moduleType != null) {
      settingsStep = groupModuleBuilder.modifyProjectTypeStep(this)
    }

    if (groupModuleBuilder == null) {
      showTemplates(group)
    }
    else if (!showCustomOptions(groupModuleBuilder)) {
      showFrameworks(group, groupModuleBuilder)
    }

    headerPanel.isVisible = headerPanel.componentCount > 0
    // align header labels
    val labels = UIUtil.findComponentsOfType<JLabel>(headerPanel, JLabel::class.java).filter({ label: JLabel -> label.isVisible && label.labelFor != null })
    var width = 0
    for (label in labels) {
      val width1 = label.preferredSize.width
      width = max(width.toDouble(), width1.toDouble()).toInt()
    }
    for (label in labels) {
      label.preferredSize = Dimension(width, label.preferredSize.height)
    }
    headerPanel.revalidate()
    headerPanel.repaint()

    updateSelection()
  }

  private fun updateSelection() {
    val template = getSelectedTemplate()
    if (template != null) {
      context.setProjectTemplate(template)
    }

    val builder = getSelectedBuilder()
    LOG.debug("builder=" + builder + "; template=" + template + "; group=" + projectTypeList.getSelectedTemplateGroup())

    context.setProjectBuilder(builder)
    if (builder != null) {
      val sequence: StepSequence = wizard.sequence
      sequence.setType(builder.builderId)
      sequence.setIgnoredSteps(builder.ignoredSteps)
    }
    wizard.setDelegate(if (builder is WizardDelegate) builder else null)
    wizard.updateWizardButtons()

    logGeneratorSelected(context)
  }

  private fun getSelectedTemplate(): ProjectTemplate? {
    return if (TEMPLATES_CARD == currentCard) templateListPanel.selectedTemplate else null
  }

  private fun getSelectedBuilder(): ModuleBuilder? {
    val template = getSelectedTemplate()
    if (template != null) {
      return builders.get(template)
    }
    val group: TemplatesGroup = projectTypeList.getSelectedTemplateGroup()
                                ?: return null

    return group.moduleBuilder
  }

  private fun fillUserTemplateMap(context: WizardContext): List<UserTemplateGroupItem> {
    if (!context.isCreatingNewProject) {
      return emptyList()
    }

    val templateItems = mutableListOf<UserTemplateGroupItem>()
    val templates = ArchivedTemplatesFactory().createTemplates(ProjectTemplatesFactory.CUSTOM_GROUP, context)

    templates.filterIsInstance<ArchivedProjectTemplate>().forEach { archivedTemplate ->
      templateItems.add(UserTemplateGroupItem(archivedTemplate))
    }

    templateItems.forEach { templateItem ->
      templatesMap.put(templateItem.group, ArrayList<ProjectTemplate>())


    }

    return templateItems
  }

  private fun fillLanguageGeneratorTemplateMap(context: WizardContext): List<LanguageGeneratorItem> {
    val generators = mutableListOf<GeneratorNewProjectWizard>()

    LanguageNewProjectWizard.EP_NAME.forEachExtensionSafe { wizard ->
      generators.add(LegacyLanguageGeneratorNewProjectWizard(context, wizard))
    }

    LanguageGeneratorNewProjectWizard.EP_NAME.forEachExtensionSafe { wizard ->
      generators.add(BaseLanguageGeneratorNewProjectWizard(context, wizard))
    }

    generators.removeIf { !it.isEnabled() }
    generators.sortBy { it.ordinal }

    if (context.isCreatingNewProject) {
      GeneratorNewProjectWizard.EP_NAME.forEachExtensionSafe { wizard ->
        generators.add(wizard)
      }
    }

    val generatorItems = generators.map { LanguageGeneratorItem(it) }
    generatorItems.forEach { generatorItem ->

      templatesMap.put(generatorItem.group, java.util.ArrayList<ProjectTemplate>())

    }

    return generatorItems
  }

  private fun installLanguageGeneratorWatcher(context: WizardContext) {
    LanguageNewProjectWizard.EP_NAME.addExtensionPointListener(object : ExtensionPointListener<LanguageNewProjectWizard> {
      override fun extensionAdded(extension: LanguageNewProjectWizard, pluginDescriptor: PluginDescriptor) {
        val generator = LegacyLanguageGeneratorNewProjectWizard(context, extension)
        val generatorItem = LanguageGeneratorItem(generator)
        projectTypeList.addLanguageGeneratorItem(generatorItem)
      }

      override fun extensionRemoved(extension: LanguageNewProjectWizard, pluginDescriptor: PluginDescriptor) {
        projectTypeList.removeLanguageGeneratorItem(extension.name)
      }
    }, context.disposable)

    LanguageGeneratorNewProjectWizard.EP_NAME.addExtensionPointListener(object : ExtensionPointListener<LanguageGeneratorNewProjectWizard> {
      override fun extensionAdded(extension: LanguageGeneratorNewProjectWizard, pluginDescriptor: PluginDescriptor) {
        val generator = BaseLanguageGeneratorNewProjectWizard(context, extension)
        val generatorItem = LanguageGeneratorItem(generator)
        projectTypeList.addLanguageGeneratorItem(generatorItem)
      }

      override fun extensionRemoved(extension: LanguageGeneratorNewProjectWizard, pluginDescriptor: PluginDescriptor) {
        projectTypeList.removeLanguageGeneratorItem(extension.name)
      }
    }, context.disposable)
  }

  private fun fillGroupTemplateMap(context: WizardContext): List<TemplateGroupItem> {
    val builders = ModuleBuilder.getAllBuilders()
    val groupMap = mutableMapOf<String, TemplatesGroup>()

    builders.forEach { builder ->
      try {
        val template = BuilderBasedTemplate(builder)
        if (builder.isTemplate) {
          val group = groupMap[builder.groupName] ?: TemplatesGroup(builder).also {
            groupMap[builder.groupName] = it
          }
          templatesMap.putValue(group, template)
        }
        else {
          val group = TemplatesGroup(builder)
          groupMap[group.name] = group

          templatesMap.put(group, java.util.ArrayList<ProjectTemplate>())

        }
      }
      catch (e: Throwable) {
        LOG.error(e)
      }
    }

    templatesMap.putAllValues(getTemplatesMap(context))

    CATEGORY_EP.extensionList.forEach { category ->
      val group = TemplatesGroup(category)
      val builder = group.moduleBuilder
      if (builder == null || builder.isAvailable) {
        templatesMap.remove(group)
        templatesMap.put(group, ArrayList<ProjectTemplate>())


      }
    }

    if (context.isCreatingNewProject) {
      val localTemplates = loadLocalTemplates()
      templatesMap.keySet().forEach { group ->
        templatesMap.putValues(group, localTemplates[group.id])
      }
    }

    val groups = templatesMap.keySet().toMutableList()

    // Sorting by module type popularity
    val moduleTypes = MultiMap<ModuleType<*>, TemplatesGroup>()
    groups.forEach { group ->
      val type = getModuleType(group)
      moduleTypes.putValue(type, group)
    }

    groups.sortWith { o1, o2 ->
      val weightDiff = o2.weight - o1.weight
      if (weightDiff != 0) return@sortWith weightDiff

      val typeSizeDiff = moduleTypes[getModuleType(o2)].size - moduleTypes[getModuleType(o1)].size
      if (typeSizeDiff != 0) return@sortWith typeSizeDiff

      o1.compareTo(o2)
    }

    val groupNames = groups.mapTo(mutableSetOf()) { it.parentGroup }

    // Move subgroups
    val subGroups = MultiMap<String, TemplatesGroup>()
    groups.listIterator().apply {
      while (hasNext()) {
        val group = next()
        val parentGroup = group.parentGroup
        if (parentGroup != null && groupNames.contains(parentGroup) && group.name != parentGroup && groupMap.containsKey(parentGroup)) {
          subGroups.putValue(parentGroup, group)
          remove()
        }
      }
    }

    groups.listIterator().apply {
      while (hasNext()) {
        val group = next()
        subGroups[group.name].forEach { add(it) }
      }
    }

    //if (PlatformUtils.isIdeaCommunity()) {
    //  groups.listIterator().apply {
    //    while (hasNext()) {
    //      val group = next()
    //      if (WebModuleTypeBase.WEB_MODULE == group.id && templatesMap[group].isEmpty()) {
    //        remove()
    //        break
    //      }
    //    }
    //  }
    //}

    return groups.map { TemplateGroupItem(it) }
  }

  private fun loadLocalTemplates(): MultiMap<String, ProjectTemplate> {
    val map = MultiMap.createConcurrent<String, ProjectTemplate>()
    TEMPLATE_EP.processWithPluginDescriptor { ep: ProjectTemplateEP, pluginDescriptor: PluginDescriptor ->
      val classLoader = pluginDescriptor.classLoader
      val url = classLoader.getResource(ep.templatePath.removePrefix("/"))
      if (url == null) {
        LOG.error(PluginException("Can't find resource for project template: " + ep.templatePath, pluginDescriptor.pluginId))
        return@processWithPluginDescriptor
      }

      try {
        val template = LocalArchivedTemplate(url, classLoader)
        if (ep.category) {
          val category = TemplateBasedCategory(template, ep.projectType)
          templatesMap.putValue(TemplatesGroup(category), template)
        }
        else {
          map.putValue(ep.projectType, template)
        }
      }
      catch (e: Exception) {
        LOG.error(PluginException("Error loading template from URL: " + ep.templatePath, e, pluginDescriptor.pluginId))
      }
    }
    return map
  }

  private fun getEmptyStatusPresenter(): Runnable {
    return Runnable {
      showCard(EMPTY_CARD)
      wizard.updateButtons(true, false, true)
    }
  }

  companion object {
    private fun getModuleType(group: TemplatesGroup): ModuleType<*>? {
      val moduleBuilder = group.moduleBuilder
      return moduleBuilder?.moduleType
    }

    val LOG = Logger.getInstance(CangNovaProjectTypeStep::class.java)

    private
    val CATEGORY_EP: ExtensionPointName<ProjectCategory> = ExtensionPointName("com.linqingying.projectWizard.projectCategory")

    private
    val TEMPLATE_EP: ExtensionPointName<ProjectTemplateEP> = ExtensionPointName("com.intellij.projectTemplate")

    private
    const val EMPTY_CARD: String = "empty card"

    private
    const val TEMPLATES_CARD: String = "templates card"

    private
    const val FRAMEWORKS_CARD: String = "frameworks card"

    //private static boolean matchFramework(ProjectCategory projectCategory, FrameworkSupportInModuleProvider framework) {
    //
    //  FrameworkRole[] roles = framework.getRoles();
    //  if (roles.length == 0) return true;
    //
    //  List<FrameworkRole> acceptable = Arrays.asList(projectCategory.getAcceptableFrameworkRoles());
    //  return ContainerUtil.intersects(Arrays.asList(roles), acceptable);
    //}
    private fun getTemplatesMap(context: WizardContext): MultiMap<TemplatesGroup, ProjectTemplate> {
      val factories = ProjectTemplatesFactory.EP_NAME.extensionList
      val groups = MultiMap<TemplatesGroup, ProjectTemplate>()
      for (factory in factories) {
        for (group in factory.groups) {
          //don't add "User-defined" node for new project wizard
          if (factory is ArchivedTemplatesFactory) {
            continue
          }

          val templates = factory.createTemplates(group, context)
          val values = listOf(*templates)
          if (values.isNotEmpty()) {
            val icon = factory.getGroupIcon(group)
            val parentGroup = factory.getParentGroup(group)
            val templatesGroup = TemplatesGroup(group, null, icon, factory.getGroupWeight(group), parentGroup, group, null)
            templatesGroup.pluginInfo = getPluginInfo(factory.javaClass)
            groups.putValues(templatesGroup, values)
          }
        }
      }
      return groups
    }

  }

  private fun getNamer(): Function<TemplateGroupItem, String> {
    return Function<TemplateGroupItem, String> { groupItem: TemplateGroupItem ->
      val keywords = LinkedHashSet<String>()
      keywords.add(groupItem.group.name)
      val builder = groupItem.group.moduleBuilder
      if (builder != null) {
        val step = customSteps.get(builder.builderId)
        if (step is NewProjectWizardStep) {
          keywords.addAll(step.keywords.toSet())
        }
      }
      java.lang.String.join(" ", keywords)
    }
  }

  private lateinit var currentCard: String

  fun getCustomStep(): ModuleWizardStep? {
    return customSteps.get(currentCard)
  }

  private fun showCard(card: String) {
    (optionsPanel.layout as CardLayout).show(optionsPanel, card)
    currentCard = card
  }

  override fun getComponent(): JComponent = panel

  override fun updateDataModel() {
    //val builder: ModuleBuilder = getSelectedBuilder()
    //if (builder != null) {
    //  wizard.getSequence().addStepsForBuilder(builder, context, myModulesProvider)
    //}
    //val step: ModuleWizardStep = getCustomStep()
    //if (step != null) {
    //  step.updateDataModel()
    //}
    //if (mySettingsStep != null) {
    //  mySettingsStep.updateDataModel()
    //}
  }

  override fun getContext(): WizardContext = context

  override fun addSettingsField(label: String, field: JComponent) {

  }

  override fun addSettingsComponent(component: JComponent) {

  }

  override fun addExpertPanel(panel: JComponent) {

  }

  override fun addExpertField(label: String, field: JComponent) {

  }

  override fun dispose() {
    // settingsStep = null
    // templatesMap.clear()
    // builders.clear()
    //customSteps.clear()
  }
}

class CangNovaNewProjectWizard(
  project: Project?, modulesProvider: ModulesProvider, defaultPath: String?,

  ) : CangNovaAbstractProjectWizard
      (
        IdeCoreBundle.message(if (project == null) "title.new.project" else "title.add.module"), project, defaultPath
      ) {

  val sequence = StepSequence()

  init {
    val pane = rootPane
    pane?.putClientProperty(UIUtil.NO_BORDER_UNDER_WINDOW_TITLE_KEY, true)
    wizardContext.modulesProvider = modulesProvider
    val projectTypeStep = CangNovaProjectTypeStep(wizardContext, this, modulesProvider)
    Disposer.register(disposable, projectTypeStep)
    sequence.addCommonStep(projectTypeStep)

    // hacky: module builder ID and module type id should start with [NPW_PREFIX], to be removed later, on migrating on new API.
    val predicate: (Set<String>) -> Boolean = { strings ->
      strings.none { it.startsWith(NPW_PREFIX) }
    }

    //mySequence.addCommonFinishingStep(new ProjectSettingsStep(wizardContext), predicate);
    for (step in sequence.getAllSteps()) {
      addStep(step!!)
    }
    super.init()
  }


}

abstract class CangNovaAbstractProjectWizard(
  @Nls title: String, project: Project?, defaultPath: String?,
) : AbstractWizard<ModuleWizardStep>(title, project) {
  val wizardContext: WizardContext = initContext(project, defaultPath, disposable)
  private var delegate: WizardDelegate? = null

  init {
    wizardContext.putUserData(KEY, this)

  }

  fun setDelegate(delegate: WizardDelegate?) {
    this.delegate = delegate
  }

  override fun getHelpID(): String? {
    val step = currentStepObject
    if (step != null) {
      return step.helpId
    }
    return null
  }

  val newProjectFilePath: String
    get() {
      return if (wizardContext.projectStorageFormat == StorageScheme.DEFAULT) {
        wizardContext.projectFileDirectory + File.separator + wizardContext.projectName + ProjectFileType.DOT_DEFAULT_EXTENSION
      }
      else {
        wizardContext.projectFileDirectory
      }
    }

  fun getBuilder(project: Project?): ProjectBuilder? {
    val builder = projectBuilder
    if (builder is ModuleBuilder) {
      if (builder.name == null) {
        builder.name = projectName
      }
      if (builder.moduleFilePath == null) {
        builder.moduleFilePath = moduleFilePath
      }
    }
    if (builder == null || !builder.validate(project, project!!)) {
      return null
    }
    return builder
  }

  val moduleFilePath: @NonNls String
    get() {
      return wizardContext.projectFileDirectory + File.separator + wizardContext.projectName + ModuleFileType.DOT_DEFAULT_EXTENSION
    }


  val projectBuilder: ProjectBuilder?
    get() {
      return wizardContext.projectBuilder
    }

  val newCompileOutput: String
    get() {
      val projectFilePath: String = wizardContext.projectFileDirectory
      var path = wizardContext.compilerOutputDirectory
      if (path == null) {
        path = if (StringUtil.endsWithChar(projectFilePath, '/')) projectFilePath + "out" else "$projectFilePath/out"
      }
      return path
    }
  val projectName: String get() = wizardContext.projectName

  val storageScheme: StorageScheme get() = wizardContext.projectStorageFormat

  companion object {
    private fun initContext(project: Project?, defaultPath: String?, parentDisposable: Disposable): WizardContext {
      var defaultPath = defaultPath
      val context = WizardContext(project, parentDisposable)
      if (defaultPath == null && project != null) {
        defaultPath = project.basePath
      }
      if (defaultPath != null) {
        context.setProjectFileDirectory(Paths.get(defaultPath), true)
        context.projectName = defaultPath.substring(FileUtil.toSystemIndependentName(defaultPath).lastIndexOf("/") + 1)
      }
      return context
    }
  }
}
